//
//  ViewController.m
//  05-列表异步加载网络图片
//
//  Created by apple on 16/1/6.
//  Copyright © 2016年 apple. All rights reserved.
//

#import "ViewController.h"
#import "AppInfo.h"
#import "AppCell.h"
#import <FBRetainCycleDetector/FBRetainCycleDetector.h>
#import <MLeaksFinder/MLeaksFinder.h>

typedef void(^block)();
@interface ViewController ()

/// 数据源数组
@property (nonatomic, strong) NSArray *dataSourceArr;
/// 队列
@property (nonatomic, strong) NSOperationQueue *queue;
/// 图片缓存
@property (nonatomic, strong) NSMutableDictionary *imageCache;
/// 操作缓存
@property (nonatomic, strong) NSMutableDictionary *operationCache;

@property (nonatomic,copy)block myblock;

@end

@implementation ViewController


#pragma mark - 懒加载
- (NSArray *)dataSourceArr
{
    if (_dataSourceArr == nil) {
        
        // 厨子 : "炒菜去!";
        _dataSourceArr = [AppInfo appInfos];
        
    }
    return _dataSourceArr;
}

- (NSOperationQueue *)queue
{
    if (_queue == nil) {
        _queue = [[NSOperationQueue alloc] init];
    }
    return _queue;
}

- (NSMutableDictionary *)imageCache
{
    if (_imageCache == nil) {
        _imageCache = [[NSMutableDictionary alloc] init];
    }
    return _imageCache;
}

- (NSMutableDictionary *)operationCache
{
    if (_operationCache == nil) {
        _operationCache = [[NSMutableDictionary alloc] init];
    }
    return _operationCache;
}

- (void)viewDidLoad {
    [super viewDidLoad];

}

/*
 1. 同步下载UI交互不流畅.  -->异步下载网络图片
 2. 异步下载网络图片时,图片不会立即展示出来.  -->使用展位图,提前懒加载imageView
 3. 重复下载图片,浪费用户流量.  -->内存缓存策略,字典内存缓存
 4. 当有网络延迟的时候,滚动cell会出先图片错乱的问题.  -->图片下载完成之后,刷新对应的cell
 5. 断网测试. : 判断image对象是否为空,当为空的时候,就不做以下操作
 [self.imageCache setObject:image forKey:app.icon];
 [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
 6. 当有网络延迟的时候,在图片下载完成之前我们来回滚动列表,还没有下载完成的图片会建立多好个下载操作,下载同一张图片
    --> 创建操作缓存池.
 7. 创建操作时使用的block,block内部使用的self(vc),我们需要注意循环引用.先分析,如果有就处理.__weak
 */

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return self.dataSourceArr.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // 这个方法是从SB中cell的标准方法.这个非常严谨.
    // 当可重用标示符设置正确,不会执行if判断.如果写错会崩溃
    AppCell *cell = [tableView dequeueReusableCellWithIdentifier:@"appCell" forIndexPath:indexPath];
    
    // 获取cell对应的模型数据
    AppInfo *app = self.dataSourceArr[indexPath.row];
    
    // 赋值
    cell.textLabel.text = app.name;
    cell.detailTextLabel.text = app.download;
    
    // 设置展位图,提前懒加载imageView
    cell.imageView.image = [UIImage imageNamed:@"user_default"];
    
    // 在建立下载操作之前,判断内存中有没有,如果有直接赋值,返回cell.如果没有,就建立异步下载操作
    if ([self.imageCache objectForKey:app.icon] != nil) {
        NSLog(@"从内存中加载...%@",app.name);
        cell.imageView.image = [self.imageCache objectForKey:app.icon];
        return cell;
    }
    
    // 在建立下载操作之前,判断下载操作存不存在
    if ([self.operationCache objectForKey:app.icon] != nil) {
        NSLog(@"%@ 正在下载中...",app.name);
        return cell;
    }
    
#pragma attention --(第一种方法)此处打破block循环的一种办法,使用weakself代替self就行修饰
   // __weak typeof(self) weakSelf = self;

    // 创建异步操作
    // 已知 : op->self(VC)
    self.myblock = ^{
            NSLog(@"从网络中加载...%@",app.name);
            // 模拟网络延迟
            if (indexPath.row > 9) {
                [NSThread sleepForTimeInterval:10];
            }
            // 同步下载图片
            NSURL *url = [NSURL URLWithString:app.icon];
            NSData *data = [NSData dataWithContentsOfURL:url];
            UIImage *image = [UIImage imageWithData:data];
            
            // 图片下载完成之后,回到主线程更新UI
            [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                if (image != nil) {
                    // 将图片保存到图片缓存中(dict),内存缓存策略
                    // 字典和数组赋值空对象
                    [self.imageCache setObject:image forKey:app.icon];
                    // 刷新对应的行
                    [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
                    // 移除操作,当图片为nil不能移除op
                }
                // 移除操作
                [self.operationCache removeObjectForKey:app.icon];
            }];
    };
    
    
    FBRetainCycleDetector *detector = [FBRetainCycleDetector new];
    [detector addCandidate:self];
    NSSet *retainCycles = [detector findRetainCycles];
    NSLog(@"self = %@", retainCycles);
    
    NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:_myblock];
    
#pragma attention --(第二种方法)此处打破block循环的关键,在使用完block后果断将其指为nil
  //  self.myblock = nil;
    // 将操作添加到操作缓存
    [self.operationCache setObject:op forKey:app.icon];
    // 将操作添加到队列
    [self.queue addOperation:op];
    
    // cell系统默认的子控件是懒加载上去的.没有赋值不会加载子控件
    // 在返回cell之前如果没有给cell的子控件赋值,他是不会加载上去的.跟列表交互的时候会调用layoutSubViews再次设置frame
    // 返回cell
    
    return cell;
}

// 点击cell获取队列中的操作个数
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSLog(@"队列中的操作个数 %tu",self.queue.operationCount);
}

// 根据是否及时调用了delloc方法来确定界面是否及时得到了释放

- (void)dealloc
{
    NSLog(@"%s",__FUNCTION__);
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end
